/*  
 * i-OSGi - Tunable Bundle Isolation for OSGi
 * Copyright (C) 2011  Sven Schulz
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.iosgi.impl.engine;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import algorithms.graphColorer.Colorer;
import dataStructures.graph.GraphColoring;

/**
 * @author Sven Schulz
 */
public class CompetitionColorer<V extends Comparable<? super V>, E> implements
		ReportingColorer<V, E> {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(CompetitionColorer.class);

	private final ExecutorService execSvc;
	private final ReportingColorer<V, E>[] colorers;

	@SuppressWarnings("unchecked")
	public CompetitionColorer(ReportingColorer<V, E> first,
			ReportingColorer<V, E>... more) {
		colorers = new ReportingColorer[1 + more.length];
		colorers[0] = first;
		System.arraycopy(more, 0, colorers, 1, more.length);
		execSvc = Executors.newCachedThreadPool();
	}

	@Override
	public GraphColoring<V, E> getColoring() {
		try {
			return this.getColoring(null, Long.MAX_VALUE, TimeUnit.DAYS);
		} catch (TimeoutException te) {
			/* Won't happen. */
			assert false;
			return null;
		}
	}

	@Override
	public GraphColoring<V, E> getColoring(final ReportListener<V, E> listener,
			final long timeout, final TimeUnit unit) throws TimeoutException {
		final AtomicReference<GraphColoring<V, E>> best = new AtomicReference<GraphColoring<V, E>>(
				null);
		final ReportListener<V, E> rl = new ReportListener<V, E>() {
			@Override
			public void handle(Colorer<V, E> colorer,
					GraphColoring<V, E> coloring) {
				synchronized (best) {
					if (best.get() == null
							|| best.get().getNumberOfColors() > coloring
									.getNumberOfColors()) {
						best.set(coloring);
						if (listener != null)
							listener.handle(colorer, coloring);
					}
				}
			}
		};
		List<Callable<Void>> competitors = new LinkedList<Callable<Void>>();
		for (final ReportingColorer<V, E> c : colorers) {
			Callable<Void> callable = new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					try {
						c.getColoring(rl, timeout, unit);
					} catch (TimeoutException te) {
						/* Ignore. Timeouts are handled by invoker. */
					} finally {
						LOGGER.debug("exiting {}", c);
					}
					return null;
				}
			};
			competitors.add(callable);
		}
		try {
			execSvc.invokeAny(competitors, timeout, unit);
			execSvc.shutdownNow();
			execSvc.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
		} catch (TimeoutException te) {
			if (best.get() == null) {
				throw te;
			}
		} catch (Exception e) {
			LOGGER.error("unhandled exception", e);
		} finally {
			LOGGER.debug("exiting {}", this);
		}
		return best.get();
	}
}